package avicit.bdp.dgs.qa.service.algo.raw.data.pcm.utils;

import avicit.bdp.dgs.qa.service.algo.raw.data.pcm.entity.PcmHeadInfo;
import avicit.bdp.dgs.qa.service.algo.raw.data.pcm.entity.PcmParamInfo;
import avicit.bdp.dgs.qa.service.algo.raw.data.pcm.entity.PcmStreamInfo;
import avicit.platform6.core.exception.BusinessException;
import cn.hutool.core.map.MapUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;

/**
 * @金航数码科技有限责任公司
 * @作者：developer
 * @邮箱：developer@avic-digital.com
 * @创建时间： 2023-11-01
 * @类说明：ReadInfConfigUtils
 * @修改记录：
 * @注意事项：
 * @主要功能：读取inf 配置文件工具类
 */
public class ReadInfConfigUtils {
    private static final Logger logger = LoggerFactory.getLogger(ReadInfConfigUtils.class);

    private String configPath;
    private Map<String, Object> inputParamsMap;

    public ReadInfConfigUtils(String configPath, Map<String, Object> inputParamsMap) {
        this.configPath = configPath;
        this.inputParamsMap = inputParamsMap;
    }

    public InputStream getCurrentInputStream() throws IOException {
        // 输入文件方式
        String inputType = MapUtil.getStr(inputParamsMap, "inputType", "local");
        InputStream configStream = null;
        if (inputType.equalsIgnoreCase("local")) {
            configStream = Files.newInputStream(Paths.get(this.configPath));
        } else if (inputType.equalsIgnoreCase("minio")) {
            String endpoint = MapUtil.getStr(inputParamsMap, "endpoint", "http://127.0.0.1:9000");
            String accessKey = MapUtil.getStr(inputParamsMap, "accessKey", "minioadmin");
            String secretKey = MapUtil.getStr(inputParamsMap, "secretKey", "minioadmin");
            String bucketName = MapUtil.getStr(inputParamsMap, "bucketName", "test");
            configStream = S3Utils.getMinioObject(endpoint, accessKey, secretKey, bucketName, configPath);
        }

        return configStream;
    }

    /**
     * 根据配置文件路径获取所有的配置信息
     */
    public Map<String, Object> getAllConfigInfo() throws IOException {
        Map<String, Object> map = new HashMap<>();
        PcmHeadInfo pcmHeadInfo = getHeadInfo();

        int n = pcmHeadInfo.getStream_number();
        List<PcmStreamInfo> streamInfoList = getPcmStreamInfoList(n);

        // 规则1.检查Stream_number参数
        if (n <= 0 || n != streamInfoList.size()) {
            String errorInfo = String.format("配置文件stream_number参数 = %s, 读取到stream info个数 = %s", n, streamInfoList.size());
            throw new BusinessException(errorInfo);
        }

        List<PcmParamInfo> pcmParamInfoList = new ArrayList<>();
        for (int i = 1; i < streamInfoList.size() + 1; i++) {
            long start = findLineNoByContent("the_parameter_description_of_stream " + i);
            long end = findLineNoByContent("the_parameter_description_of_stream " + (i + 1));
            List<PcmParamInfo> paramInfoList = getPcmParamInfoList(start, end, 0);
            pcmParamInfoList.addAll(paramInfoList);
            streamInfoList.get(i - 1).setParams(paramInfoList);
        }

        for (PcmStreamInfo pcmStreamInfo : streamInfoList) {
            for (PcmParamInfo paramInfo : pcmStreamInfo.getParams()) {
                if (paramInfo.getRate() >= pcmStreamInfo.getSubframe_deepth()) {
                    int[] frame_no = new int[paramInfo.getFrame_no().length];
                    paramInfo.setFrame_no(frame_no);
                }
            }
        }
        pcmHeadInfo.setStreamInfoList(streamInfoList);
        map.put("pcmStream", streamInfoList);
        map.put("headInfo", pcmHeadInfo);
        map.put("paraminfo", pcmParamInfoList);

        return map;
    }

    /**
     * 根据配置文件获取PCM 配置头信息
     *
     * @throws IOException
     */
    public PcmHeadInfo getHeadInfo() throws IOException {
        BufferedReader br = null;
        try {

            br = new BufferedReader(new InputStreamReader(getCurrentInputStream()));
            String data = null;
            PcmHeadInfo pcmHeadInfo = new PcmHeadInfo();
            while ((data = br.readLine()) != null) {
                if (data.contains("Header_informations:")) {
                    ;
                } else if (data.contains("Create_date")) {
                    pcmHeadInfo.setCreate_date(data.split(":")[1]);
                } else if (data.contains("test_id")) {
                    pcmHeadInfo.setTest_id(data.split(":")[1]);
                } else if (data.contains("stream_number")) {
                    pcmHeadInfo.setStream_number(NumberUntils.StrToInt(data.split(":")[1]));
                } else if (data.contains("derpar_number")) {
                    pcmHeadInfo.setDerpar_number(NumberUntils.StrToInt(data.split(":")[1]));
                } else if (data.contains("The format description of stream")) {
                    break;
                }
            }
            return pcmHeadInfo;
        } catch (Exception e) {
            logger.error("获取PCM配置信息-头定义信息实体失败", e);
            throw new BusinessException("获取PCM配置信息-头定义信息实体失败，错误信息：" +  e.getMessage());
        } finally {
            if (br != null) {
                br.close();
            }
        }
    }

    /**
     * 获取PCM数据流定义
     *
     * @param n 参数个数
     * @throws IOException
     */
    public List<PcmStreamInfo> getPcmStreamInfoList(int n) throws IOException {
        List<PcmStreamInfo> pcmStreamInfoList = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            BufferedReader br = null;
            try {
                br = new BufferedReader(new InputStreamReader(getCurrentInputStream()));
                String data = null;
                boolean flag = false;
                PcmStreamInfo pcmStreamInfo = null;
                while ((data = br.readLine()) != null) {
                    if (data.contains("The format description of stream " + (i + 1))) {
                        pcmStreamInfo = new PcmStreamInfo();
                        flag = true;
                    } else if (data.contains("aircraft_name") && flag) {
                        pcmStreamInfo.setAircraft_name(data.split(":")[1]);
                    } else if (data.contains("test_name") && flag) {
                        pcmStreamInfo.setTest_name(data.split(":")[1]);
                    } else if (data.contains("parameter_number") && flag) {
                        pcmStreamInfo.setParameter_number(NumberUntils.StrToInt(data.split(":")[1]));
                    } else if (data.contains("bit_rate") && flag) {
                        pcmStreamInfo.setBit_rate(NumberUntils.StrToFloat(data.split(":")[1]));
                    } else if (data.contains("pcm_code") && flag) {
                        pcmStreamInfo.setPcm_code(data.split(":")[1]);
                    } else if (data.contains("loop_width") && flag) {
                        pcmStreamInfo.setLoop_width(NumberUntils.StrToFloat(data.split(":")[1]));
                    } else if (data.trim().indexOf("word_length") == 0 && flag) {
                        pcmStreamInfo.setWord_length(NumberUntils.StrToInt(data.split(":")[1]));
                    } else if (data.contains("parity") && flag) {
                        pcmStreamInfo.setParity(data.split(":")[1]);
                    } else if (data.contains("Onboard_acq._equipments_type") && flag) {
                        pcmStreamInfo.setEquipments_type(data.split(":")[1]);
                    } else if (data.contains("frame_length") && flag) {
                        pcmStreamInfo.setFrame_length(NumberUntils.StrToInt(data.split(":")[1]));
                    } else if (data.contains("subframe_deepth") && flag) {
                        pcmStreamInfo.setSubframe_deepth(NumberUntils.StrToInt(data.split(":")[1]));
                    } else if (data.contains("major_frame_rate") && flag) {
                        pcmStreamInfo.setMajor_frame_rate(NumberUntils.StrToInt(data.split(":")[1]));
                    } else if (data.trim().contains("_word_length") && flag) {
                        pcmStreamInfo.setSync_word_length(NumberUntils.StrToInt(data.split(":")[1]));
                    } else if (data.contains("sync_pattern") && flag) {
                        pcmStreamInfo.setSync_pattern(data.split(":")[1]);
                    } else if (data.contains("id_position") && flag) {
                        pcmStreamInfo.setId_position(NumberUntils.StrToInt(data.split(":")[1]));
                    } else if (data.contains("id_lsb") && flag) {
                        pcmStreamInfo.setId_lsb(NumberUntils.StrToInt(data.split(":")[1]));
                    } else if (data.contains("dating_type") && flag) {
                        pcmStreamInfo.setDating_type(data.split(":")[1]);
                    } else if (data.contains("time_interpolation") && flag) {
                        pcmStreamInfo.setTime_interpolation(NumberUntils.StrToInt(data.split(":")[1]));
                    } else if (data.contains("output_polarity") && flag) {
                        pcmStreamInfo.setOutput_polarity(data.split(":")[1]);
                    } else if (data.contains("input_source") && flag) {
                        pcmStreamInfo.setInput_source(data.split(":")[1]);
                    } else if (data.contains("input_polarity") && flag) {
                        pcmStreamInfo.setInput_polarity(data.split(":")[1]);
                    } else if (data.contains("bits_order") && flag) {
                        pcmStreamInfo.setBits_order(data.split(":")[1]);
                    } else if (data.contains("save_last_frame") && flag) {
                        pcmStreamInfo.setSave_last_frame(NumberUntils.StrToInt(data.split(":")[1]));
                    } else if (data.contains("sync_strategy") && flag) {
                        pcmStreamInfo.setSync_strategy(data.split(":")[1]);
                    } else if (data.contains("sync_location") && flag) {
                        pcmStreamInfo.setSync_location(NumberUntils.StrToInt(data.split(":")[1]));
                    } else if (data.contains("subframe_mode") && flag) {
                        pcmStreamInfo.setSubframe_mode(data.split(":")[1]);
                    } else if (data.contains("direction") && flag) {
                        pcmStreamInfo.setDirection(data.split(":")[1]);
                    } else if (data.contains("first_value") && flag) {
                        pcmStreamInfo.setFirst_value(NumberUntils.StrToInt(data.split(":")[1]));
                    } else if (data.contains("pattern_loop") && flag) {
                        pcmStreamInfo.setPattern_loop(NumberUntils.StrToInt(data.split(":")[1]));
                    } else if (data.contains("The format description of stream " + (i + 2)) && flag) {
                        pcmStreamInfoList.add(pcmStreamInfo);
                        break;
                    } else if (data.contains("the_parameter_description")) {
                        pcmStreamInfoList.add(pcmStreamInfo);
                        break;
                    }
                }
            } catch (Exception e) {
                logger.error("获取PCM配置信息-数据流定义失败", e);
                throw new BusinessException("获取PCM配置信息-数据流定义失败，错误信息：" +  e.getMessage());
            } finally {
                br.close();
            }
        }

        return pcmStreamInfoList;
    }

    /**
     * 根据单个PCM数据流定义开始位置与结束位置获取数据流参数信息
     *
     * @param start 参数开始定义行号
     * @param end   参数结束定义行数
     * @param n     n
     */
    public List<PcmParamInfo> getPcmParamInfoList(long start, long end, int n) throws IOException {
        List<PcmParamInfo> paramInfoList = new ArrayList<>();

        BufferedReader br = null;
        long counter = 0;
        PcmParamInfo paramInfo = null;
        try {
            br = new BufferedReader(new InputStreamReader(getCurrentInputStream()));

            String data;
            List<String> code_phyList = null;
            while ((data = br.readLine()) != null) {
                counter++;
                if (counter >= start && counter <= end) {
                    if (data.contains("serial_no.")) {
                        paramInfo = new PcmParamInfo();
                        code_phyList = new ArrayList<>();
                        paramInfo.setSerial_no(NumberUntils.StrToInt(data.split(":")[1]));
                    } else if (data.contains("id_no.")) {
                        paramInfo.setId_no(NumberUntils.StrToInt(data.split(":")[1]));
                    } else if (data.contains("name")) {
                        paramInfo.setName(NumberUntils.getParamValue(data));
                    } else if (data.contains("unit")) {
                        paramInfo.setUnit(NumberUntils.getParamValue(data));
                    } else if (data.contains("cal_type")) {
                        paramInfo.setCal_type(NumberUntils.getParamValue(data));
                    } else if (data.contains("code_type")) {
                        paramInfo.setCode_type(NumberUntils.getParamValue(data));
                    } else if (data.contains("cal_date")) {
                        paramInfo.setCal_date(NumberUntils.getParamValue(data));
                    } else if (data.contains("cal_number")) {
                        paramInfo.setCal_number(NumberUntils.getParamValue(data));
                    } else if (data.contains("rate:")) {
                        paramInfo.setRate(NumberUntils.StrToInt(data.split(":")[1]));
                    } else if (data.contains("word_interval")) {
                        paramInfo.setWord_interval(NumberUntils.StrToInt(data.split(":")[1]));
                    } else if (data.contains("sign_bit")) {
                        paramInfo.setSign_bit(NumberUntils.StrToInt(data.split(":")[1]));
                    } else if (data.contains("inverse")) {
                        paramInfo.setInverse(NumberUntils.StrToInt(data.split(":")[1]));
                    } else if (data.contains("pcm_words")) {
                        paramInfo.setPcm_words(NumberUntils.StrToInt(data.split(":")[1]));
                    } else if (data.contains("frame_no")) {
                        String[] arr = data.split(":")[1].trim().split(",");
                        int[] array = Arrays.stream(arr).mapToInt(Integer::parseInt).toArray();
                        paramInfo.setFrame_no(array);
                    } else if (data.contains("word_no")) {
                        String[] arr = data.split(":")[1].trim().split(",");
                        int[] array = Arrays.stream(arr).mapToInt(Integer::parseInt).toArray();
                        paramInfo.setWord_no(array);
                    } else if (data.contains("source_bit")) {
                        String[] arr = data.split(":")[1].trim().split(",");
                        int[] array = Arrays.stream(arr).mapToInt(Integer::parseInt).toArray();
                        paramInfo.setSource_bit(array);
                    } else if (data.contains("bit_length")) {
                        String[] arr = data.split(":")[1].trim().split(",");
                        int[] array = Arrays.stream(arr).mapToInt(Integer::parseInt).toArray();
                        paramInfo.setBit_length(array);
                    } else if (data.contains("coefficient")) {
                        String[] arr = data.split(":")[1].trim().split(",");
                        double[] array = Arrays.stream(arr).mapToDouble(Double::parseDouble).toArray();
                        paramInfo.setCoefficient(array);
                    } else if (data.contains("segment_number")) {
                        paramInfo.setSegment_number(NumberUntils.StrToInt(data.split(":")[1]));
                    } else if (data.contains("code,phy")) {
                        code_phyList.add(data.split(":")[1]);
                    } else if (data.contains("Description:")) {
                        if (code_phyList.size() > 0 && paramInfo.getSegment_number() > 0) {
                            double[] code = new double[code_phyList.size()];
                            double[] phy = new double[code_phyList.size()];

                            for (int i = 0; i < code_phyList.size(); i++) {
                                code[i] = NumberUntils.StrToDouble(code_phyList.get(i).split(",")[0]);
                                phy[i] = NumberUntils.StrToDouble(code_phyList.get(i).split(",")[1]);
                            }

                            paramInfo.setCodes(code);
                            paramInfo.setPhys(phy);
                            paramInfo.segment_coefficients = ComputeUtils.getSegemCoefficient(paramInfo);
                        }
                        paramInfo.setDescription(data.split(":").length == 2 ? data.split(":")[1] : "");
                        paramInfoList.add(paramInfo);
                    }
                } else if (counter > end) {
                    break;
                }
            }

        } catch (Exception e) {
            logger.error("根据单个PCM数据流定义开始位置与结束位置，获取数据流参数信息失败", e);
            throw new BusinessException("根据单个PCM数据流定义开始位置与结束位置，获取数据流参数信息失败", e);
        } finally {
            br.close();
        }

        return paramInfoList;
    }

    /**
     * 根据查询内容在文本中获取第一次出现的行号
     *
     * @param content content
     */
    public long findLineNoByContent(String content) throws IOException {
        long counter = 0;
        try (BufferedReader br = new BufferedReader(new InputStreamReader(getCurrentInputStream()))) {
            String data;
            while ((data = br.readLine()) != null) {
                counter++;
                if (data.contains(content)) {
                    break;
                }
            }
            return counter;
        } catch (Exception e) {
            logger.error("根据查询内容在文本中获取第一次出现的行号失败", e);
            return counter;
        }
    }
}
